package org.biogroovy.io;

import groovy.util.logging.Slf4j

import java.util.concurrent.Callable
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.ThreadPoolExecutor

@Slf4j
public class FetchManager {
	
	private int initPoolSize = 3;
	private File outputDir = null;
	private String ext = "";
	
	ThreadPoolExecutor exec = null;
	
	/**
	 * Constructor.
	 */
	public FetchManager() {
		init(3, new File(System.getProperty("user.home"), ".biogroovy"), "");
	}
	
	/**
	 * Constructor.
	 * @param threadPoolSize the initial number of threads in the threadpool
	 */
	public FetchManager(int threadPoolSize) {
		init(threadPoolSize, null, "")
	}
	
	/**
	 * Constructor.
	 * @param threadPoolSize the initial number of threads in the threadpool
	 * @param outputDir the output directory
	 */
	public FetchManager(int threadPoolSize, File outputDir) {
		init(threadPoolSize, outputDir, "")
	}
	
	/**
	 * Constructor.
	 * @param threadPoolSize the initial number of threads in the threadpool
	 * @param outputDir the output directory
	 * @param ext the file extension.
	 */
	public FetchManager(int threadPoolSize, File outputDir, String ext) {
		init(threadPoolSize, outputDir, ext)
	}
	
	/**
	 * This method initializes the FetchManager.
	 * @param threadPoolSize the initial number of threads in the threadpool
	 * @param outputDir the output directory
	 * @param ext the file extension.
	 */
	private void init(int threadPoolSize, File outputDir, ext) {
		this.outputDir = outputDir;
		this.initPoolSize = threadPoolSize;
		this.ext = ext;
		exec = Executors.newFixedThreadPool(initPoolSize);
	}
	
	
	/**
	 * This method fetches a file.  It creates and queues up all FileFetchTasks.
	 * @param fetcher the fetcher used to fetch the data and create the file.
	 * @param outputDir the optional output directory (defaults to the user.home/.biogroovy directory)
	 * @param ext the optional file extension (defaults to an empty string)
	 * @param ids the ids of the data to be downloaded
	 */
	public void downloadFiles(IFetcher<?> fetcher, String ext, File outputDir, String... ids) {
		if(outputDir != null) {
			this.outputDir = outputDir;
		}
		
		if(ext != null) {
			this.ext = ext;
		}
		List<Future<Void>> futureList = new ArrayList<>();
		for(String id: ids) {
			IFetcher nuFetcher = fetcher.getNewInstance();
			FileFetchTask task = new FileFetchTask(nuFetcher, this.outputDir, this.ext, id);
			Future<Void> future = exec.submit(task);
			futureList.add(future);
		}
		
		for(Future<Void> currFuture: futureList) {
			try {
				currFuture.get();
			}catch(Exception ex) {
				log.error(ex.getMessage(), ex);
			}
			
		}
		
	}
	
	
	/**
	 * This class is used to fetch data from a datasource and write the results out to a file.
	 */
	 public class FileFetchTask implements Callable<Void>{
		
		/** The fetcher for the file. */
		IFetcher fetcher;
		
		/** The output directory. */
		File outputDir;
		
		/** The file extension. */
		String ext;
		
		/** The ID of the data item to be downloaded. */
		String id=''
		
		/**
		 * Constructor
		 * @param fetcher the class responsible for fetching the data and writing out a file.
		 * @param outputDir the optional output directory
		 * @param ext the optional file extension.
		 * @param ids the IDs of the items to be downloaded
		 */
		public FileFetchTask(IFetcher<?> fetcher, File outputDir, String ext, String id) {
			this.fetcher = fetcher;
			this.outputDir = outputDir;
			this.id = id;
			this.ext = ext;
		}

		@Override
		public Void call() throws Exception {
			log.info("Downloading files:")
			fetcher.fetchAsFiles(this.outputDir, this.id);
			return null;
		}
		
	}
}
